'''
Created on 2010/07/08

@author: saburo
'''

from pyrfid.Util.Util import Util
import binascii

import sys

class Compactor(object):
    
    COMPACTION_TYPE_INT = 1
    COMPACTION_TYPE_NUMERIC = 2
    COMPACTION_TYPE_BIT5 = 3
    COMPACTION_TYPE_BIT6 = 4
    COMPACTION_TYPE_BIT7 = 5
    COMPACTION_TYPE_UTF8 = 6 
        
    
    def __init__(self):
        pass
    

    @staticmethod
    def compactObject(objectValue, compactionParameter):
        """docstring for compactObject"""
        
        precursor = str()
        strHex = str()
        string_length_of_compacted_object = str()
        
        if compactionParameter == 0: # Application defined
            precursor = '00000000'
            strHex = objectValue
        elif compactionParameter == 2:  # UTF-8 data
            precursor = '01110000'
            strHex = objectValue
        elif compactionParameter == 3:  # Packed Objects            
            strHex = binascii.a2b_hex(objectValue)            
        elif compactionParameter == 15: # Defined in 15961, but not in 1562
            raise "Non Implemented"
        else:
            ct = Compactor.chooseCompactType(objectValue)
            print ct
            if ct == Compactor.COMPACTION_TYPE_INT:
                strHex = Compactor.intCompact(objectValue)
                precursor = '00010000'
            elif ct == Compactor.COMPACTION_TYPE_NUMERIC:
                strHex = Compactor.numericCompact(objectValue)
                precursor = '00100000'
            elif ct == Compactor.COMPACTION_TYPE_BIT5:
                strHex = Compactor.bit5Compact(objectValue)
                precursor = '00110000'
            elif ct == Compactor.COMPACTION_TYPE_BIT6:
                strHex = Compactor.bit6Compact(objectValue)
                precursor = '01000000'
            elif ct == Compactor.COMPACTION_TYPE_BIT7:
                strHex = Compactor.bit7Compact(objectValue)
                precursor = '01010000'
            elif ct == Compactor.COMPACTION_TYPE_UTF8:
                strHex = objectValue
                precursor = '01100000'
            else:
                raise "compactTypeError"
            
            string_length_of_compacted_object = Compactor.getObjectLength(strHex)
        return {'precursor':precursor, 'object':strHex, 'lengthOfObject':string_length_of_compacted_object }
    
    
    @staticmethod
    def getObjectLength(compacted_object):
        
        lenHex = str()
        lenBit = str()
        
        # Compact the length of the compacted object value
        length = len(compacted_object) / 2
        if length >= 0 and length <= 15:
            lenHex = '0' + hex(length)[2:].upper()
        elif length >= 16 and length <= 127:
            lenHex = hex(length)[2:].upper()
        elif length >= 128 and length <= 16383:
            for c in hex(length)[2:].upper():
                lenBit += Util.h2b.get(c)
            lenHexLower = Util.convertBitToHex('0' + lenBit[-7:])
            if len(lenBit[:-7]) > 7:
                lenBitHigher = lenBit[-14:-7]
            else:
                lenBitHigher = lenBit[:-7]
            lenBitHigher = '1' + '0'*(7 - len(lenBitHigher)) + lenBitHigher
            lenHexHigher = Util.convertBitToHex(lenBitHigher)
            lenHex = lenHexHigher + lenHexLower
        elif length >= 16384 and length <= 2097151:
            for c in hex(length)[2:].upper():
                lenBit += Util.h2b.get(c)
            lenHexLower = Util.convertBitToHex('0' + lenBit[-7:])
            lenHexMiddle = Util.convertBitToHex('0' + lenBit[-14:-7])
            if len(lenBit[:-14]) > 7:
                lenBitHigher = lenBit[-21:-14]
            else:
                lenBitHigher = lenBit[:-14]
            lenBitHigher = '1' + '0'*(7 - len(lenBitHigher)) + lenBitHigher
            lenHexHigher = Util.convertBitToHex(lenBitHigher)
            lenHex = lenHexLower + lenHexMiddle + lenHexHigher
        
        return lenHex
        
    

    ########################################################################################
    #    COMPACTION
    ########################################################################################
    @staticmethod
    def numericCompact(obj):
        """Numeric compaction is designed to encode any decimal numeric character string, including leading zeros."""    
        strHex = str()
        listBit = list()
        strInt = str()

        if len(obj) < 4:
            raise "numCharLengthError"
        for i in range(len(obj) / 2):
            if obj[:2] >= '30' and obj[:2] <= '39':
                listBit.append(Util.h2b.get(binascii.a2b_hex(obj[:2])))
            else:
                raise "numCharError"
            obj = obj[2:]
        
        if len(listBit)%2 == 1:
            listBit.append('1111')

        strHex = ''.join(map(Util.b2h.get, listBit))
        return strHex
    
    @staticmethod
    def bit5Compact(obj):
        """5-bit compaction is designed to encode uppercase Latin characters and some punctuation."""
        strBit = str()
        strHex = str()

        for i in range(len(obj) / 2):
            
            if int(obj[:2], 16) >= int('41', 16) and int(obj[:2], 16) <= int('5F', 16):
                bitString = (Util.h2b.get(obj[0]))[3:] + Util.h2b.get(obj[1])
                strBit += bitString
            else:
                
                print int('41', 16) , " ",  int('5F', 16)
                print int(obj[:2], 16)
                raise "charError"
            obj = obj[2:]

        if len(strBit) % 8 != 0:
            pad = len(strBit) % 8
            strBit += '0' * (8 - pad)
        strHex = Util.convertBitToHex(strBit)

        return strHex
    
    @staticmethod
    def bit6Compact(obj):
        """6-bit compaction is designed to encode uppercase Latin characters,
        numeric digits and some punctuation."""
        strBit = str()

        for i in range(len(obj) / 2):
            bitString = ''
            if obj[:2] >= '20' and obj[:2] <= '5f':
                bitString += Util.h2b.get(obj[0])[2:]
                bitString += Util.h2b.get(obj[1])
                strBit += bitString
            else:
                raise "charError"
            obj = obj[2:]
        pad = len(strBit) % 8
        if pad == 2:
            strBit += '100000'
        elif pad == 4:
            strBit += '1000'
        elif pad == 6:
            strBit += '10'
        strHex = Util.convertBitToHex(strBit)
        return strHex
    
    @staticmethod
    def bit7Compact(obj):
        """7-bit compaction is designed to encode all ISO/IEC 646 characters
        including control characters except for DELETE."""

        bitString = str()
        strBit = str()

        for i in range(len(obj) / 2):
            if obj[:2] >= '00' and obj[:2] <= '7e' and obj[:2] != '10':
                bitString += Util.h2b.get(obj[0])[1:]
                bitString += Util.h2b.get(obj[1])
                strBit += bitString
                bitString = ''
            else:
                raise "charError"
            obj = obj[2:]
        pad = len(strBit) % 8
        if pad != 0:
            strBit += '1' * (8 - pad)
        strHex = Util.convertBitToHex(strBit)
        return strHex
    
    @staticmethod
    def octetCompact(obj):
        strHex = obj
        return strHex
    
    @staticmethod
    def chooseCompactType(obj):
        ct = 3
        b5 = False
        b6 = False
        b7 = False
        is_int = False
        numeric = False    # If leading byte is '30', numeric = True
        utf8 = False
        object_copy = obj
        lenObj = len(obj) / 2
        if lenObj < 2:
            ct = 6
            return ct
        
        int_obj = int(obj[:2], 16)
        
        for i in range(lenObj - 1):
            #numeric
            if int_obj >= int('30', 16) and int_obj <= int('39', 16):
                numeric = True
            elif int_obj >= int('41', 16) and int_obj <= int('5F', 16):
                b5 = True
            elif int_obj >= int('20', 16) and int_obj <= int('5F', 16):
                b6 = True
            elif int_obj >= int('00', 16) and int_obj <= int('7E', 16):
                b7 = True
            else:
                utf8 = True
            
            obj = obj[2:]
            int_obj = int(obj[:2], 16)

        if b7 == True:
            return Compactor.COMPACTION_TYPE_BIT7
        elif b6 == True:
            return Compactor.COMPACTION_TYPE_BIT6
        elif b5 == True:
            return Compactor.COMPACTION_TYPE_BIT5
        elif numeric == True:            
            if object_copy[:2] == '30':
                return Compactor.COMPACTION_TYPE_NUMERIC
            
            if int(object_copy, 16) > 10 or int(object_copy, 16) < 9999999999999999999: 
                return Compactor.COMPACTION_TYPE_INT
            else:
                return Compactor.COMPACTION_TYPE_NUMERIC
        else:
            raise "Exception"
    
        
    @staticmethod
    def intCompact(obj):
        """Integer compaction is designed to compact decimal integers form value 10
        to 9999999999999999999."""
        
        strInt = str()
#       if obj[:2] == '30':
#           raise "compactTypeError"
        
        for i in range(len(obj) / 2):
            if obj[:2] >= '30' and obj[:2] <= '39':
                strInt += binascii.a2b_hex(obj[:2])
            else:
                raise "intError"
            obj = obj[2:]
        
        intInt = int(strInt)
        strHex = Util.convertIntToHexNotMax(intInt)
        if len(strHex) % 2 != 0:
            strHex = '0'+strHex
        return strHex    
    